/*  GStruct: data structure helpers for Java
 *  Copyright (C) 2011 Giulio Franco
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package gstruct.extensions;

import gstruct.assoc.LazyPipeMap;
import gstruct.single.DefaultComparator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

/**
 * Extension functions for associative containers (Maps)
 * @author Giulio Franco <giulio_franco@msn.com>
 */
public final class Assoc {
	private Assoc() { }
	
	/**
	 * Flips a map, returning a multimap of values to keys.
	 * @param input		Input map
	 * @return	Flipped map, so that .get(v) returns a collection of all the keys
	 * 			for which <pre>input.get(key).equals(v)</pre>
	 */
	public static <K,V> Map<V, ? extends Collection<K>> flip(Map<K,V> input) {
		Map<V, ArrayList<K>> res = new HashMap<V, ArrayList<K>>();
		
		for (Map.Entry<K, V> ent : input.entrySet()) {
			ArrayList<K> lst = res.get(ent.getValue());
			if (lst == null) {
				lst = new ArrayList<K>(2);
				res.put(ent.getValue(), lst);
			}
			lst.add(ent.getKey());
		}
		
		return res;
	}
	
	/**
	 * Flips a map, returning a map of values to keys.
	 * @param input		Input map
	 * @return	Flipped map, so that .get(v) returns the smallest keys
	 * 			for which <pre>input.get(key).equals(v)</pre>
	 * 			Comparisons are made according to keys' natural comparator.
	 */
	public static <K extends Comparable<? extends K>,V> Map<V,K> flipMin(Map<K,V> input) {
		Comparator<K> comp = DefaultComparator.getInstance();
		return flipMin(input, comp);
	}
	
	/**
	 * Flips a map, returning a map of values to keys.
	 * @param input		Input map
	 * @param comp		Comparator to order keys
	 * @return	Flipped map, so that .get(v) returns the smallest keys
	 * 			for which <pre>input.get(key).equals(v)</pre>
	 * 			Comparisons are made according to the given Comparator
	 */
	public static <K,V> Map<V,K> flipMin(Map<K,V> input, Comparator<K> comp) {
		Map<V, K> res = new HashMap<V, K>();
		
		for (Map.Entry<K, V> ent : input.entrySet()) {
			K curVal = res.get(ent.getValue());
			if (curVal == null || comp.compare(ent.getKey(), curVal) < 0) {
				res.put(ent.getValue(), ent.getKey());
			}
		}
		
		return res;
	}
	
	/**
	 * Returns a piped view of two maps
	 * @param m1	Map from K1 to K2
	 * @param m2	Map from K2 to V
	 * @return	Piped view map, so that .get(k1).equals(v) iff
	 * 			a k2 exists so that
	 * 			<pre>m1.get(k1).equals(k2) && m2.get(k2).equals(v)</pre> 
	 */
	public static <K1, K2, V> Map<K1, V> pipeView(
			Map<K1, K2> m1, Map<K2, V> m2) {
		return new LazyPipeMap<>(m1, m2);
	}
}
